// Copyright (c) 2024 Go-Frame-Lite
// Go-Frame-Lite is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package gerror

import (
	"fmt"
	"runtime"
	"strings"
)

// Error 接口定义
// 类似于GF框架的gcode.Code接口
// 提供统一的错误码和错误信息管理

type Error interface {
	// Code 返回错误码
	Code() int
	// Message 返回错误信息
	Message() string
	// Detail 返回错误详情
	Detail() string
	// Stack 返回调用栈信息
	Stack() string
	// Error 实现error接口
	Error() string
	// SetDetail 设置错误详情
	SetDetail(detail string) Error
	// SetStack 设置调用栈信息
	SetStack(stack string) Error
}

// gError 实现Error接口
// 类似于GF框架的gerror

type gError struct {
	code    int      // 错误码
	message string   // 错误信息
	detail  string   // 错误详情
	stack   string   // 调用栈信息
}

// New 创建一个新的错误
// 类似于GF框架的gerror.New
func New(code int, message string) Error {
	err := &gError{
		code:    code,
		message: message,
	}
	err.stack = getStack()
	return err
}

// Newf 创建一个带格式化信息的错误
// 类似于GF框架的gerror.Newf
func Newf(code int, format string, args ...interface{}) Error {
	err := &gError{
		code:    code,
		message: fmt.Sprintf(format, args...),
	}
	err.stack = getStack()
	return err
}

// Wrap 包装一个错误
// 类似于GF框架的gerror.Wrap
func Wrap(err error, code int, message string) Error {
	if err == nil {
		return nil
	}
	
	var detail string
	if e, ok := err.(Error); ok {
		detail = e.Error()
	} else {
		detail = err.Error()
	}
	
	gErr := &gError{
		code:    code,
		message: message,
		detail:  detail,
	}
	gErr.stack = getStack()
	return gErr
}

// Wrapf 包装一个错误，支持格式化
// 类似于GF框架的gerror.Wrapf
func Wrapf(err error, code int, format string, args ...interface{}) Error {
	if err == nil {
		return nil
	}
	
	var detail string
	if e, ok := err.(Error); ok {
		detail = e.Error()
	} else {
		detail = err.Error()
	}
	
	gErr := &gError{
		code:    code,
		message: fmt.Sprintf(format, args...),
		detail:  detail,
	}
	gErr.stack = getStack()
	return gErr
}

// Code 返回错误码
func (e *gError) Code() int {
	return e.code
}

// Message 返回错误信息
func (e *gError) Message() string {
	return e.message
}

// Detail 返回错误详情
func (e *gError) Detail() string {
	return e.detail
}

// Stack 返回调用栈信息
func (e *gError) Stack() string {
	return e.stack
}

// Error 实现error接口
func (e *gError) Error() string {
	if e.detail != "" {
		return fmt.Sprintf("code: %d, message: %s, detail: %s", e.code, e.message, e.detail)
	}
	return fmt.Sprintf("code: %d, message: %s", e.code, e.message)
}

// SetDetail 设置错误详情
func (e *gError) SetDetail(detail string) Error {
	e.detail = detail
	return e
}

// SetStack 设置调用栈信息
func (e *gError) SetStack(stack string) Error {
	e.stack = stack
	return e
}

// getStack 获取调用栈信息
func getStack() string {
	const depth = 32
	var pcs [depth]uintptr
	n := runtime.Callers(3, pcs[:]) // 跳过当前函数和调用者
	
	if n == 0 {
		return ""
	}
	
	frames := runtime.CallersFrames(pcs[:n])
	var stack strings.Builder
	
	for {
		frame, more := frames.Next()
		if !strings.Contains(frame.File, "runtime/") && 
		   !strings.Contains(frame.File, "go-frame-lite/pkg/errors/") {
			stack.WriteString(fmt.Sprintf("\n%s\n\t%s:%d", frame.Function, frame.File, frame.Line))
		}
		if !more {
			break
		}
	}
	
	return stack.String()
}

// Is 检查错误是否匹配指定的错误码
// 类似于GF框架的gerror.Is
func Is(err error, code int) bool {
	if err == nil {
		return false
	}
	
	if e, ok := err.(Error); ok {
		return e.Code() == code
	}
	
	return false
}

// CodeFrom 从错误中提取错误码
// 如果错误不是Error类型，返回默认错误码
func CodeFrom(err error, defaultCode int) int {
	if err == nil {
		return defaultCode
	}
	
	if e, ok := err.(Error); ok {
		return e.Code()
	}
	
	return defaultCode
}

// MessageFrom 从错误中提取错误信息
// 如果错误不是Error类型，返回错误字符串
func MessageFrom(err error) string {
	if err == nil {
		return ""
	}
	
	if e, ok := err.(Error); ok {
		return e.Message()
	}
	
	return err.Error()
}